package main

import (
	"fmt"
	"math"
	"sort"
	"time"
)

const (
	// 时间格式，方便解析和打印
	TimeLayout = "2006-01-02 15:04:05"
	HourLayout = "15:04"
)

// --- 核心常量 (可配置) ---
const (
	MinSafeLevelM3      = 2.0           // 最低安全水量 (m³)
	MaxSafeLevelM3      = 12.0          // 最高安全水量 (m³)
	InflowRateM3PerHour = 4.0           // 进水速度 (m³/h)
	PlanningHorizon     = 3 * time.Hour // 每次规划的核心时间窗口

	// 【新增】魔法数字常量化 - 提高代码可读性
	MinRefillAmountM3    = 0.01            // 最小补水量阈值 (m³)
	MinAvailableHours    = 0.5             // 最小可用时间 (小时)
	MaxAdjustAttempts    = 10              // 调度器最大调整尝试次数
	ConsecutiveGapLimit  = 5 * time.Minute // 判断连续禁补时段的最大间隔
	OverflowSafetyMargin = 0.1             // 防溢出安全边际
)

// --- 数值处理辅助函数 ---

// round2 保留2位小数
func round2(value float64) float64 {
	return math.Round(value*100) / 100
}

// multiply 乘法运算，结果保留2位小数
func multiply(a, b float64) float64 {
	return round2(a * b)
}

// divide 除法运算，结果保留2位小数
func divide(a, b float64) float64 {
	if b == 0 {
		return 0
	}
	return round2(a / b)
}

// --- 日志级别定义 ---
type LogLevel int

const (
	LogLevelError LogLevel = iota // 错误级别
	LogLevelWarn                  // 警告级别
	LogLevelInfo                  // 信息级别
	LogLevelDebug                 // 调试级别
)

// 全局日志级别，可在生产环境中设置为 LogLevelWarn 或 LogLevelError
var currentLogLevel = LogLevelDebug

// logMessage 统一的日志输出函数
func logMessage(level LogLevel, format string, args ...interface{}) {
	if level <= currentLogLevel {
		prefix := ""
		switch level {
		case LogLevelError:
			prefix = "[ERROR] "
		case LogLevelWarn:
			prefix = "[WARN]  "
		case LogLevelInfo:
			prefix = "[INFO]  "
		case LogLevelDebug:
			prefix = "[DEBUG] "
		}
		fmt.Printf(prefix+format, args...)
	}
}

// 【新增】辅助函数：打印分隔线
func logSeparator(level LogLevel, char string, length int) {
	if level <= currentLogLevel {
		separator := ""
		for i := 0; i < length; i++ {
			separator += char
		}
		logMessage(level, "%s\n", separator)
	}
}

// 【新增】辅助函数：打印标题
func logTitle(level LogLevel, title string) {
	if level <= currentLogLevel {
		logSeparator(level, "=", 60)
		logMessage(level, "   %s\n", title)
		logSeparator(level, "=", 60)
	}
}

// 【新增】辅助函数：打印子标题
func logSubTitle(level LogLevel) {
	if level <= currentLogLevel {
		logSeparator(level, "-", 60)
	}
}

// --- 数据结构定义 ---

// TimeRange 定义一个时间范围
type TimeRange struct {
	Start time.Time
	End   time.Time
}

// Contains 检查给定时间是否在此范围内 [Start, End)
func (tr TimeRange) Contains(t time.Time) bool {
	return !t.Before(tr.Start) && t.Before(tr.End)
}

// TargetedTimeRange 带目标水位的特殊时间段
type TargetedTimeRange struct {
	TimeRange
	TargetLevelM3 float64
}

// UserRules 用户自定义的规则
type UserRules struct {
	PriorityPeriods  []TargetedTimeRange // 优先补水
	PeakPeriods      []TargetedTimeRange // 用水高峰
	ForbiddenPeriods []TimeRange         // 不可补水
}

// SystemState 某一时刻的系统状态，作为规划器的输入
type SystemState struct {
	CurrentTime         time.Time
	CurrentLevelM3      float64
	Prediction3h        []float64 // 未来24小时的8个3h步长预测用水量
	Prediction30min     []float64 // 未来24小时的48个30min步长预测用水量
	NextDayPrediction3h []float64 // [可选] 第二天 00:00-06:00 的预测
	BlockStartTime      time.Time // 【新增】第一个预测块的标准起始时间（如09:00）
}

// ReplenishmentTask 生成的补水任务
type ReplenishmentTask struct {
	StartTime time.Time
	EndTime   time.Time
	AmountM3  float64
	Reason    string // 任务生成原因，便于调试和追踪
	Priority  string // 任务优先级
}

// ScheduleError 调度错误信息
type ScheduleError struct {
	Code    string
	Message string
}

func (e *ScheduleError) Error() string {
	return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}

// 【新增】PlanningPeriod 规划周期，包含实际的开始和结束时间
type PlanningPeriod struct {
	Start         time.Time
	End           time.Time
	IsPartial     bool      // 是否是部分时间块
	BlockIndex    int       // 在预测数组中的索引
	BlockStart    time.Time // 该块的标准开始时间（如09:00）
	UsageFraction float64   // 使用该块预测值的比例（用于部分时间块）
}

// Planner 智能补水计划器
type Planner struct {
	rules        UserRules
	minSafeLevel float64
	maxSafeLevel float64
	inflowRate   float64
}

// NewPlanner 创建一个新的计划器实例
func NewPlanner(rules UserRules, min, max, inflow float64) *Planner {
	if inflow <= 0 {
		panic("Planner creation failed: inflowRate must be a positive number.")
	}
	return &Planner{
		rules:        rules,
		minSafeLevel: min,
		maxSafeLevel: max,
		inflowRate:   inflow,
	}
}

// 【修改】移除了重复的溢出检查代码，因为现在 scheduleTask 内部已经处理
func (p *Planner) GenerateLastPlan(state SystemState) []*ReplenishmentTask {
	logMessage(LogLevelInfo, "\n--- [SPECIAL PLANNING - LAST PERIOD] at %s ---\n", state.CurrentTime.Format(TimeLayout))

	nextDayPrediction3h := state.NextDayPrediction3h
	if len(nextDayPrediction3h) == 0 {
		logMessage(LogLevelWarn, "Warning: No next-day prediction available. Using current prediction instead.\n")

		if len(state.Prediction3h) >= 2 {
			nextDayPrediction3h = state.Prediction3h[len(state.Prediction3h)-2:]
		} else {
			nextDayPrediction3h = []float64{2.0, 2.0}
		}
	}

	usageNextMorning := nextDayPrediction3h[0] + nextDayPrediction3h[1]
	logMessage(LogLevelDebug, "  - Next day's morning (00:00-06:00) predicted usage: %.2f m³\n", usageNextMorning)

	targetLevelAtMidnight := usageNextMorning + p.minSafeLevel
	logMessage(LogLevelDebug, "  - Target level at midnight: %.2f m³ (Usage) + %.2f m³ (Safe) = %.2f m³\n",
		usageNextMorning, p.minSafeLevel, targetLevelAtMidnight)

	usageCurrentPeriod := 0.0
	if len(state.Prediction3h) > 0 {
		usageCurrentPeriod = state.Prediction3h[0]
	}

	logMessage(LogLevelDebug, "  - Current period predicted usage: %.2f m³\n", usageCurrentPeriod)

	predictedLevelAtMidnight := state.CurrentLevelM3 - usageCurrentPeriod

	amountNeeded := targetLevelAtMidnight - predictedLevelAtMidnight
	logMessage(LogLevelDebug, "  - Amount needed: %.2f m³ (Target) - %.2f m³ (Predicted) = %.2f m³\n",
		targetLevelAtMidnight, predictedLevelAtMidnight, amountNeeded)

	if amountNeeded <= MinRefillAmountM3 {
		logMessage(LogLevelInfo, "Conclusion: No special replenishment needed for the next day.\n")
		return nil
	}

	isSafety := predictedLevelAtMidnight >= p.minSafeLevel

	// 【修改】直接调用 scheduleTask，溢出检查已经内置
	tasks := p.scheduleTask(state, amountNeeded, isSafety, "为第二天凌晨准备")

	if tasks == nil || len(tasks) == 0 {
		logMessage(LogLevelWarn, "Conclusion: Replenishment was needed for the next day, but scheduler could not find a valid time slot.\n")
		return nil
	}

	logMessage(LogLevelInfo, "Conclusion: Special replenishment task(s) for next day generated.\n")
	return tasks
}

func (p *Planner) GeneratePlan(state SystemState) []*ReplenishmentTask {
	logMessage(LogLevelInfo, "\n--- [PLANNING] at %s ---\n", state.CurrentTime.Format(TimeLayout))

	// ========== 第一轮：需求计算 ==========
	logMessage(LogLevelDebug, "\n=== 第一轮：需求计算 ===\n")

	safetyReq := p.calculateSafetyRequirement(state)
	logMessage(LogLevelDebug, "V_req_safety: %.2f m³\n", safetyReq)

	proactiveReq := p.calculateProactiveRequirement(state)
	logMessage(LogLevelDebug, "V_req_proactive: %.2f m³\n", proactiveReq)

	opportunityReq := p.calculateOpportunityRequirement(state)
	logMessage(LogLevelDebug, "V_req_opportunity: %.2f m³\n", opportunityReq)

	// ========== 第二轮：决策合成 ==========
	logMessage(LogLevelDebug, "\n=== 第二轮：决策合成 ===\n")

	baseNeeded := math.Max(safetyReq, proactiveReq)
	logMessage(LogLevelDebug, "V_base_needed: %.2f m³\n", baseNeeded)

	theoreticalAmount := math.Max(baseNeeded, opportunityReq)
	logMessage(LogLevelDebug, "Theoretical V_final (before scheduling & correction): %.2f m³\n", theoreticalAmount)

	if theoreticalAmount < MinRefillAmountM3 {
		logMessage(LogLevelInfo, "Conclusion: No replenishment needed based on requirements.\n")
		return nil
	}

	// ========== 第三轮：任务调度 ==========
	logMessage(LogLevelDebug, "\n=== 第三轮：任务调度与时间检查 ===\n")

	reason := "常规补水"
	if safetyReq > 0 {
		reason = "安全需求"
	} else if proactiveReq > 0 {
		reason = "前瞻性准备"
	} else if opportunityReq > 0 {
		reason = "优先补水机会"
	}

	isSafety := safetyReq == 0

	// 【修改】scheduleTask 内部已经处理溢出检查
	tasks := p.scheduleTask(state, theoreticalAmount, isSafety, reason)
	if tasks == nil || len(tasks) == 0 {
		logMessage(LogLevelWarn, "Conclusion: Replenishment was needed, but scheduler could not find a valid time slot.\n")
		return nil
	}

	// 【保留】对所有任务进行时间充足性检查
	// 【删除】溢出检查（已在 scheduleTask 内部完成）
	adjustedTasks := []*ReplenishmentTask{}

	for _, task := range tasks {
		// ========== 检查时间是否充足 ==========
		// 【保留】这个检查确保任务时长和补水量匹配
		taskDuration := task.EndTime.Sub(task.StartTime).Hours()
		requiredDuration := divide(task.AmountM3, p.inflowRate)

		if requiredDuration > multiply(taskDuration, 1.1) {
			shortage := multiply(requiredDuration-taskDuration, p.inflowRate)
			logMessage(LogLevelWarn, "  - WARNING: Insufficient time for sub-task! Theoretical amount %.2f m³ needs %.2f h, but only %.2f h available. Short by %.2f m³\n",
				task.AmountM3, requiredDuration, taskDuration, shortage)

			task.AmountM3 = multiply(taskDuration, p.inflowRate)
			logMessage(LogLevelInfo, "  - Adjusted sub-task amount to %.2f m³ (max capacity in available time)\n", task.AmountM3)
		}

		// 只要任务通过了时间检查，就可以添加
		if task.AmountM3 > MinRefillAmountM3 {
			adjustedTasks = append(adjustedTasks, task)
		}
	}

	if len(adjustedTasks) == 0 {
		logMessage(LogLevelWarn, "Conclusion: All tasks canceled after adjustments.\n")
		return nil
	}

	logMessage(LogLevelInfo, "Conclusion: Replenishment task(s) generated.\n")
	return adjustedTasks
}

// calculateSafetyRequirement 计算安全需求
func (p *Planner) calculateSafetyRequirement(state SystemState) float64 {
	if len(state.Prediction3h) == 0 {
		return 0.0
	}

	usageNext3h := state.Prediction3h[0]
	predictedLevelAtEnd := state.CurrentLevelM3 - usageNext3h

	if predictedLevelAtEnd < p.minSafeLevel {
		return round2(p.minSafeLevel - predictedLevelAtEnd)
	}

	return 0
}

// calculateProactiveRequirement 计算前瞻性需求
func (p *Planner) calculateProactiveRequirement(state SystemState) float64 {
	forbidPrep := p.calculateForbidPrep(state)
	logMessage(LogLevelDebug, "  - V_forbid_prep: %.2f m³\n", forbidPrep)

	peakPrep := p.calculatePeakPrep(state)
	logMessage(LogLevelDebug, "  - V_peak_prep: %.2f m³\n", peakPrep)

	return math.Max(forbidPrep, peakPrep)
}

// calculateForbidPrep 为不可补水时段做准备
func (p *Planner) calculateForbidPrep(state SystemState) float64 {
	nextForbid, found := findNextPeriod(state.CurrentTime, p.rules.ForbiddenPeriods)
	if !found {
		return 0
	}

	usageUntilForbid := SumPredictedUsage(state.CurrentTime, state.CurrentTime, nextForbid.Start, state.Prediction3h)

	totalForbidUsage := 0.0
	currentForbidEnd := nextForbid.End
	consecutiveCount := 1

	logMessage(LogLevelDebug, "    - Analyzing consecutive forbidden periods starting from %s:\n", nextForbid.Start.Format(HourLayout))

	usageDuringForbid := SumPredictedUsage(state.CurrentTime, nextForbid.Start, nextForbid.End, state.Prediction3h)
	totalForbidUsage = round2(totalForbidUsage + usageDuringForbid)
	logMessage(LogLevelDebug, "      [%d] %s -> %s: %.2f m³\n",
		consecutiveCount, nextForbid.Start.Format(HourLayout), nextForbid.End.Format(HourLayout), usageDuringForbid)

	for {
		nextConsecutiveForbid, found := findImmediateNextPeriod(currentForbidEnd, p.rules.ForbiddenPeriods)
		if !found {
			break
		}

		gap := nextConsecutiveForbid.Start.Sub(currentForbidEnd)
		if gap > ConsecutiveGapLimit {
			logMessage(LogLevelDebug, "      - Gap of %.1f minutes detected. Not consecutive.\n", gap.Minutes())
			break
		}

		consecutiveCount++
		usageDuringThisForbid := SumPredictedUsage(state.CurrentTime, nextConsecutiveForbid.Start, nextConsecutiveForbid.End, state.Prediction3h)
		totalForbidUsage = round2(totalForbidUsage + usageDuringThisForbid)
		logMessage(LogLevelDebug, "      [%d] %s -> %s: %.2f m³ (consecutive)\n",
			consecutiveCount, nextConsecutiveForbid.Start.Format(HourLayout), nextConsecutiveForbid.End.Format(HourLayout), usageDuringThisForbid)

		currentForbidEnd = nextConsecutiveForbid.End
	}

	logMessage(LogLevelDebug, "    - Total consecutive forbidden periods: %d, total usage: %.2f m³\n", consecutiveCount, totalForbidUsage)

	minLevelAtForbidStart := totalForbidUsage + p.minSafeLevel
	predictedLevelAtForbidStart := state.CurrentLevelM3 - usageUntilForbid
	totalDeficit := minLevelAtForbidStart - predictedLevelAtForbidStart

	if totalDeficit <= 0 {
		return 0
	}

	_, currentBlockEnd, _ := getCurrentStandardBlock(state.CurrentTime)

	windowStart := currentBlockEnd // 从当前规划窗口结束开始
	windowEnd := nextForbid.Start
	futureCapacity := p.calculateFutureRefillCapacity(windowStart, windowEnd, p.rules)
	remainingDeficit := totalDeficit - futureCapacity

	return math.Max(0, round2(remainingDeficit))
}

// calculatePeakPrep 为用水高峰做准备
func (p *Planner) calculatePeakPrep(state SystemState) float64 {
	nextPeak, found := findNextTargetedPeriod(state.CurrentTime, p.rules.PeakPeriods)
	if !found {
		return 0
	}

	usageUntilPeak := SumPredictedUsage(state.CurrentTime, state.CurrentTime, nextPeak.Start, state.Prediction3h)
	predictedLevelAtPeakStart := state.CurrentLevelM3 - usageUntilPeak
	totalDeficit := nextPeak.TargetLevelM3 - predictedLevelAtPeakStart

	if totalDeficit <= 0 {
		return 0
	}

	_, currentBlockEnd, _ := getCurrentStandardBlock(state.CurrentTime)

	windowStart := currentBlockEnd // 从当前规划窗口结束开始
	windowEnd := nextPeak.Start
	futureCapacity := p.calculateFutureRefillCapacity(windowStart, windowEnd, p.rules)
	remainingDeficit := totalDeficit - futureCapacity

	return math.Max(0, round2(remainingDeficit))
}

// calculateOpportunityRequirement 计算机会性需求
// 【修改】对跨窗口的优先时段进行截取，并调整目标水位
func (p *Planner) calculateOpportunityRequirement(state SystemState) float64 {
	_, currentBlockEnd, _ := getCurrentStandardBlock(state.CurrentTime)
	planningWindow := TimeRange{Start: state.CurrentTime, End: currentBlockEnd}

	// 找出所有与规划窗口有交集的优先时段，并进行截取
	var overlappingPeriods []TargetedTimeRange
	for _, period := range p.rules.PriorityPeriods {
		// 计算时段与规划窗口的交集
		actualStart := period.Start
		if actualStart.Before(planningWindow.Start) {
			actualStart = planningWindow.Start
		}

		actualEnd := period.End
		needsAdjustment := false
		if actualEnd.After(planningWindow.End) {
			actualEnd = planningWindow.End
			needsAdjustment = true // 标记需要调整目标
		}

		// 检查是否有有效的交集
		if actualStart.Before(actualEnd) && actualEnd.After(planningWindow.Start) {
			adjustedTarget := period.TargetLevelM3

			// 【关键】如果时段跨越窗口边界，需要调整目标水位
			if needsAdjustment {
				// 计算窗口结束到原时段结束之间的预测用水量
				futureUsage := SumPredictedUsage(state.CurrentTime, actualEnd, period.End, state.Prediction3h)
				// 调整后的目标 = 原目标 + 未来用水量
				// 这样可以确保到窗口结束时有足够的水来应对后续用水
				adjustedTarget = round2(period.TargetLevelM3 + futureUsage)

				logMessage(LogLevelDebug, "      Period [%s -> %s] crosses window boundary, truncated to [%s -> %s]\n",
					period.Start.Format(HourLayout), period.End.Format(HourLayout),
					actualStart.Format(HourLayout), actualEnd.Format(HourLayout))
				logMessage(LogLevelDebug, "      Original target: %.2f m³, Future usage (%s -> %s): %.2f m³, Adjusted target: %.2f m³\n",
					period.TargetLevelM3, actualEnd.Format(HourLayout), period.End.Format(HourLayout),
					futureUsage, adjustedTarget)
			}

			adjustedPeriod := TargetedTimeRange{
				TimeRange:     TimeRange{Start: actualStart, End: actualEnd},
				TargetLevelM3: adjustedTarget,
			}
			overlappingPeriods = append(overlappingPeriods, adjustedPeriod)
		}
	}

	if len(overlappingPeriods) == 0 {
		logMessage(LogLevelDebug, "    - No priority periods within current planning window [%s -> %s]\n",
			planningWindow.Start.Format(HourLayout),
			planningWindow.End.Format(HourLayout))
		return 0
	}

	logMessage(LogLevelDebug, "    - Found %d priority period(s) within planning window [%s -> %s]\n",
		len(overlappingPeriods),
		planningWindow.Start.Format(HourLayout),
		planningWindow.End.Format(HourLayout))

	// 按结束时间排序，确保按时序处理
	sort.Slice(overlappingPeriods, func(i, j int) bool {
		return overlappingPeriods[i].End.Before(overlappingPeriods[j].End)
	})

	// 模拟补水轨迹，计算累积需求
	cumulativeRefillNeeded := 0.0

	for idx, period := range overlappingPeriods {
		// 计算到该时段结束时的预测用水量
		usageUntilEnd := SumPredictedUsage(state.CurrentTime, state.CurrentTime, period.End, state.Prediction3h)

		// 计算如果现在补充cumulativeRefillNeeded水量后，到该时段结束时的水位
		predictedLevelAtEnd := state.CurrentLevelM3 + cumulativeRefillNeeded - usageUntilEnd

		// 计算该时段还需要多少水才能达到目标
		amountNeeded := period.TargetLevelM3 - predictedLevelAtEnd

		logMessage(LogLevelDebug, "      [%d] Period [%s -> %s] target=%.2f m³:\n",
			idx+1, period.Start.Format(HourLayout), period.End.Format(HourLayout), period.TargetLevelM3)
		logMessage(LogLevelDebug, "          Usage until end: %.2f m³\n", usageUntilEnd)
		logMessage(LogLevelDebug, "          Predicted level (with %.2f m³ refill): %.2f m³\n",
			cumulativeRefillNeeded, predictedLevelAtEnd)
		logMessage(LogLevelDebug, "          Additional needed: %.2f m³\n", amountNeeded)

		// 如果需要更多水，累加到总需求中
		if amountNeeded > 0 {
			cumulativeRefillNeeded = round2(cumulativeRefillNeeded + amountNeeded)
			logMessage(LogLevelDebug, "          -> Cumulative refill updated to: %.2f m³\n", cumulativeRefillNeeded)
		}
	}

	if cumulativeRefillNeeded <= 0 {
		logMessage(LogLevelDebug, "    - Current trajectory already meets or exceeds all priority targets.\n")
		return 0
	}

	// 检查规划窗口内的可用时间容量
	availableHours := planningWindow.End.Sub(state.CurrentTime).Hours()
	if availableHours < 0 {
		availableHours = 0
	}

	maxCapacityInTime := multiply(availableHours, p.inflowRate)
	logMessage(LogLevelDebug, "    - Available time in planning window: %.2f hours (%.2f m³ capacity)\n",
		availableHours, maxCapacityInTime)

	// 考虑防溢出：计算到规划窗口结束时的最大允许补水量
	usageUntilWindowEnd := SumPredictedUsage(state.CurrentTime, state.CurrentTime, planningWindow.End, state.Prediction3h)
	maxAllowableRefill := p.maxSafeLevel - (state.CurrentLevelM3 - usageUntilWindowEnd)

	if maxAllowableRefill > 0 && maxAllowableRefill < maxCapacityInTime {
		logMessage(LogLevelDebug, "    - Overflow protection: Limiting refill to %.2f m³ (to avoid exceeding %.2f m³)\n",
			maxAllowableRefill, p.maxSafeLevel)
		maxCapacityInTime = maxAllowableRefill
	}

	// 取累积需求和最大容量中的较小值
	opportunityAmount := math.Min(cumulativeRefillNeeded, maxCapacityInTime)

	logMessage(LogLevelDebug, "    - Final opportunity requirement: %.2f m³\n", opportunityAmount)
	logMessage(LogLevelDebug, "      (Cumulative needed: %.2f m³, Capacity limit: %.2f m³)\n",
		cumulativeRefillNeeded, maxCapacityInTime)

	return math.Max(0, round2(opportunityAmount))
}

// calculateFutureRefillCapacity 计算在一个时间窗口内，理论上可补充的最大水量
func (p *Planner) calculateFutureRefillCapacity(start, end time.Time, rules UserRules) float64 {
	if !end.After(start) {
		return 0.0
	}

	totalWindow := TimeRange{Start: start, End: end}
	totalForbiddenDuration := time.Duration(0)

	for _, forbidPeriod := range rules.ForbiddenPeriods {
		overlap := intersectionDuration(totalWindow, forbidPeriod)
		totalForbiddenDuration += overlap
	}

	totalDuration := totalWindow.End.Sub(totalWindow.Start)
	refillableDuration := totalDuration - totalForbiddenDuration

	if refillableDuration < 0 {
		refillableDuration = 0
	}

	refillableHours := refillableDuration.Hours()

	return round2(multiply(refillableHours, p.inflowRate))
}

// scheduleTask 调度具体的补水任务
// splitTaskByForbiddenPeriods 将任务按禁补时段拆分成多个子任务
func (p *Planner) splitTaskByForbiddenPeriods(task *ReplenishmentTask, forbiddenPeriods []TimeRange) []*ReplenishmentTask {
	if task == nil {
		return nil
	}

	// 找出所有与任务时间范围重叠的禁补时段
	var overlappingForbidden []TimeRange
	for _, forbid := range forbiddenPeriods {
		if intersectionDuration(TimeRange{Start: task.StartTime, End: task.EndTime}, forbid) > 0 {
			// 确保禁补时段完全在任务时间范围内（不在边界上）
			if forbid.Start.After(task.StartTime) && forbid.End.Before(task.EndTime) {
				overlappingForbidden = append(overlappingForbidden, forbid)
			}
		}
	}

	// 如果没有中间的禁补时段，返回原任务
	if len(overlappingForbidden) == 0 {
		return []*ReplenishmentTask{task}
	}

	// 按开始时间排序禁补时段
	sort.Slice(overlappingForbidden, func(i, j int) bool {
		return overlappingForbidden[i].Start.Before(overlappingForbidden[j].Start)
	})

	logMessage(LogLevelDebug, "  - Task Splitter: Found %d forbidden period(s) in the middle of task. Splitting...\n", len(overlappingForbidden))

	// 拆分任务
	var tasks []*ReplenishmentTask
	currentStart := task.StartTime
	remainingAmount := task.AmountM3

	for _, forbid := range overlappingForbidden {
		// 在禁补时段之前创建一个子任务
		if forbid.Start.After(currentStart) {
			segmentDuration := forbid.Start.Sub(currentStart)
			segmentHours := segmentDuration.Hours()
			segmentAmount := round2(multiply(segmentHours, p.inflowRate))

			if segmentAmount > remainingAmount {
				segmentAmount = remainingAmount
			}

			if segmentAmount > MinRefillAmountM3 {
				tasks = append(tasks, &ReplenishmentTask{
					StartTime: currentStart,
					EndTime:   forbid.Start,
					AmountM3:  segmentAmount,
					Reason:    task.Reason,
					Priority:  task.Priority,
				})
				remainingAmount = round2(remainingAmount - segmentAmount)

				logMessage(LogLevelDebug, "    - Sub-task: [%s -> %s] %.2f m³\n",
					currentStart.Format(HourLayout), forbid.Start.Format(HourLayout), segmentAmount)
			}
		}

		// 跳过禁补时段
		currentStart = forbid.End
		logMessage(LogLevelDebug, "    - Skipping forbidden period: [%s -> %s]\n",
			forbid.Start.Format(HourLayout), forbid.End.Format(HourLayout))
	}

	// 添加最后一段
	if currentStart.Before(task.EndTime) && remainingAmount > MinRefillAmountM3 {
		segmentDuration := task.EndTime.Sub(currentStart)
		segmentHours := segmentDuration.Hours()
		segmentAmount := round2(multiply(segmentHours, p.inflowRate))

		if segmentAmount > remainingAmount {
			segmentAmount = remainingAmount
		}

		if segmentAmount > MinRefillAmountM3 {
			tasks = append(tasks, &ReplenishmentTask{
				StartTime: currentStart,
				EndTime:   task.EndTime,
				AmountM3:  segmentAmount,
				Reason:    task.Reason,
				Priority:  task.Priority,
			})

			logMessage(LogLevelDebug, "    - Sub-task: [%s -> %s] %.2f m³\n",
				currentStart.Format(HourLayout), task.EndTime.Format(HourLayout), segmentAmount)
		}
	}

	if len(tasks) == 0 {
		logMessage(LogLevelWarn, "  - Task Splitter WARNING: No valid sub-tasks after splitting.\n")
		return nil
	}

	logMessage(LogLevelInfo, "  - Task Splitter: Split into %d sub-task(s)\n", len(tasks))
	return tasks
}

func (p *Planner) scheduleTask(state SystemState, amountM3 float64, isSafety bool, reason string) []*ReplenishmentTask {
	planningWindowStart := state.CurrentTime
	_, planningWindowEnd, _ := getCurrentStandardBlock(state.CurrentTime)

	maxPossibleRefill := p.calculateFutureRefillCapacity(planningWindowStart, planningWindowEnd, p.rules)

	adjustedAmountM3 := amountM3
	if adjustedAmountM3 > maxPossibleRefill {
		logMessage(LogLevelWarn, "  - Scheduler WARNING: Required refill (%.2f m³) exceeds max capacity (%.2f m³). Capping amount.\n",
			adjustedAmountM3, maxPossibleRefill)
		adjustedAmountM3 = maxPossibleRefill
	}

	if adjustedAmountM3 <= MinRefillAmountM3 {
		logMessage(LogLevelDebug, "  - Scheduler: Adjusted amount too small (%.4f m³), canceling task.\n", adjustedAmountM3)
		return nil
	}

	// 【新增】先计算所有可补水的时间段
	availableSlots := p.getAvailableTimeSlots(planningWindowStart, planningWindowEnd, p.rules.ForbiddenPeriods)

	if len(availableSlots) == 0 {
		logMessage(LogLevelError, "  - Scheduler ERROR: No available time slots in planning window.\n")
		return nil
	}

	// 【修改】传入 state 参数以支持溢出检查
	return p.distributeRefillAcrossSlots(availableSlots, adjustedAmountM3, isSafety, reason, planningWindowStart, planningWindowEnd, state)
}

// 【新增】获取所有可补水的时间段
func (p *Planner) getAvailableTimeSlots(start, end time.Time, forbiddenPeriods []TimeRange) []TimeRange {
	var slots []TimeRange
	currentStart := start

	for currentStart.Before(end) {
		// 查找下一个禁补时段
		nextForbid, found := findNextPeriod(currentStart, forbiddenPeriods)

		if !found || nextForbid.Start.After(end) || !nextForbid.Start.Before(end) {
			// 没有更多禁补时段，或者禁补时段在窗口之外
			if currentStart.Before(end) {
				slots = append(slots, TimeRange{Start: currentStart, End: end})
			}
			break
		}

		// 添加禁补之前的时间段
		if nextForbid.Start.After(currentStart) {
			slotEnd := nextForbid.Start
			if slotEnd.After(end) {
				slotEnd = end
			}
			slots = append(slots, TimeRange{Start: currentStart, End: slotEnd})
		}

		// 跳过禁补时段
		currentStart = nextForbid.End
		if currentStart.After(end) {
			break
		}
	}

	return slots
}

// 【修改】添加了 state 参数并在每个任务生成时进行溢出检查
func (p *Planner) distributeRefillAcrossSlots(slots []TimeRange, totalAmount float64, isSafety bool, reason string, windowStart, windowEnd time.Time, state SystemState) []*ReplenishmentTask {
	var tasks []*ReplenishmentTask
	remainingAmount := totalAmount

	// 【新增】累计已生成任务的补水量，用于溢出检查
	accumulatedRefill := 0.0

	isASAP := !isSafety

	if isASAP {
		// ASAP策略：从前往后填充
		for _, slot := range slots {
			if remainingAmount <= MinRefillAmountM3 {
				break
			}

			slotDuration := slot.End.Sub(slot.Start).Hours()
			slotCapacity := multiply(slotDuration, p.inflowRate)

			amountInThisSlot := math.Min(remainingAmount, slotCapacity)
			if amountInThisSlot <= MinRefillAmountM3 {
				continue
			}

			taskDuration := time.Duration(multiply(divide(amountInThisSlot, p.inflowRate), float64(time.Hour)))
			taskEndTime := slot.Start.Add(taskDuration)

			// 【新增】溢出检查 - 计算任务结束时的预期水位
			usageUntilTaskEnd := SumPredictedUsage(state.CurrentTime, state.CurrentTime, taskEndTime, state.Prediction3h)
			predictedEndLevel := state.CurrentLevelM3 + accumulatedRefill + amountInThisSlot - usageUntilTaskEnd

			// 【新增】如果会溢出，调整补水量
			if predictedEndLevel > p.maxSafeLevel {
				overflowAmount := predictedEndLevel - p.maxSafeLevel
				// 添加安全边际以避免边界情况
				overflowAmount = round2(overflowAmount + OverflowSafetyMargin)

				logMessage(LogLevelWarn, "  - Overflow Check: Task [%s -> %s] would cause overflow (predicted %.2f m³ > max %.2f m³)\n",
					slot.Start.Format(HourLayout), taskEndTime.Format(HourLayout),
					predictedEndLevel, p.maxSafeLevel)

				amountInThisSlot = round2(amountInThisSlot - overflowAmount)

				// 如果调整后的量太小，跳过这个任务
				if amountInThisSlot <= MinRefillAmountM3 {
					logMessage(LogLevelWarn, "  - Overflow Check: Adjusted amount too small (%.4f m³), skipping this slot.\n", amountInThisSlot)
					continue
				}

				// 重新计算任务时长
				taskDuration = time.Duration(multiply(divide(amountInThisSlot, p.inflowRate), float64(time.Hour)))
				taskEndTime = slot.Start.Add(taskDuration)

				logMessage(LogLevelInfo, "  - Overflow Check: Reduced refill amount to %.2f m³ to prevent overflow.\n", amountInThisSlot)
			}

			task := &ReplenishmentTask{
				StartTime: slot.Start,
				EndTime:   taskEndTime,
				AmountM3:  round2(amountInThisSlot),
				Reason:    reason,
				Priority:  getPriority(isSafety, reason),
			}

			tasks = append(tasks, task)
			accumulatedRefill = round2(accumulatedRefill + amountInThisSlot)
			remainingAmount = round2(remainingAmount - amountInThisSlot)
		}
	} else {
		// ALAP策略：从后往前填充
		for i := len(slots) - 1; i >= 0; i-- {
			if remainingAmount <= MinRefillAmountM3 {
				break
			}

			slot := slots[i]
			slotDuration := slot.End.Sub(slot.Start).Hours()
			slotCapacity := multiply(slotDuration, p.inflowRate)

			amountInThisSlot := math.Min(remainingAmount, slotCapacity)
			if amountInThisSlot <= MinRefillAmountM3 {
				continue
			}

			taskDuration := time.Duration(multiply(divide(amountInThisSlot, p.inflowRate), float64(time.Hour)))
			taskStartTime := slot.End.Add(-taskDuration)
			taskEndTime := slot.End

			// 【新增】溢出检查 - 计算任务结束时的预期水位
			// 注意：对于ALAP策略，我们需要考虑之前（时间上更早）生成的所有任务
			totalRefillSoFar := accumulatedRefill + amountInThisSlot
			usageUntilTaskEnd := SumPredictedUsage(state.CurrentTime, state.CurrentTime, taskEndTime, state.Prediction3h)
			predictedEndLevel := state.CurrentLevelM3 + totalRefillSoFar - usageUntilTaskEnd

			// 【新增】如果会溢出，调整补水量
			if predictedEndLevel > p.maxSafeLevel {
				overflowAmount := predictedEndLevel - p.maxSafeLevel
				// 添加安全边际
				//overflowAmount = round2(overflowAmount + OverflowSafetyMargin)

				logMessage(LogLevelWarn, "  - Overflow Check: Task [%s -> %s] would cause overflow (predicted %.2f m³ > max %.2f m³)\n",
					taskStartTime.Format(HourLayout), taskEndTime.Format(HourLayout),
					predictedEndLevel, p.maxSafeLevel)

				amountInThisSlot = round2(amountInThisSlot - overflowAmount)

				// 如果调整后的量太小，跳过这个任务
				if amountInThisSlot <= MinRefillAmountM3 {
					logMessage(LogLevelWarn, "  - Overflow Check: Adjusted amount too small (%.4f m³), skipping this slot.\n", amountInThisSlot)
					continue
				}

				// 重新计算任务时长和开始时间
				taskDuration = time.Duration(multiply(divide(amountInThisSlot, p.inflowRate), float64(time.Hour)))
				taskStartTime = slot.End.Add(-taskDuration)

				logMessage(LogLevelInfo, "  - Overflow Check: Reduced refill amount to %.2f m³ to prevent overflow.\n", amountInThisSlot)
			}

			task := &ReplenishmentTask{
				StartTime: taskStartTime,
				EndTime:   taskEndTime,
				AmountM3:  round2(amountInThisSlot),
				Reason:    reason,
				Priority:  getPriority(isSafety, reason),
			}

			// 因为是从后往前，需要插入到前面
			tasks = append([]*ReplenishmentTask{task}, tasks...)
			accumulatedRefill = round2(accumulatedRefill + amountInThisSlot)
			remainingAmount = round2(remainingAmount - amountInThisSlot)
		}
	}

	if len(tasks) == 0 {
		logMessage(LogLevelError, "  - Scheduler ERROR: Failed to generate any tasks (possibly due to overflow constraints).\n")
		return nil
	}

	return tasks
}

func getPriority(isSafety bool, reason string) string {
	if !isSafety {
		return "P0-紧急"
	} else if reason == "前瞻性准备" {
		return "P1-前瞻"
	} else if reason == "优先补水机会" {
		return "P2-优先"
	}
	return "P4-常规"
}

// findConflict 用于查找时间冲突
func (p *Planner) findConflict(taskRange TimeRange, forbiddenPeriods []TimeRange) (TimeRange, bool) {
	for _, forbid := range forbiddenPeriods {
		if intersectionDuration(taskRange, forbid) > 0 {
			return forbid, true
		}
	}
	return TimeRange{}, false
}

// --- 辅助函数 ---

// findNextPeriod 查找未来24小时内最近的一个时间段
func findNextPeriod(currentTime time.Time, periods []TimeRange) (TimeRange, bool) {
	var nextPeriod TimeRange
	found := false
	minDiff := 25 * time.Hour

	for _, p := range periods {
		if p.Start.After(currentTime) {
			diff := p.Start.Sub(currentTime)
			if diff < minDiff {
				minDiff = diff
				nextPeriod = p
				found = true
			}
		}
	}
	return nextPeriod, found
}

// findNextTargetedPeriod 查找未来24小时内最近的一个带目标的时间段
func findNextTargetedPeriod(fromTime time.Time, periods []TargetedTimeRange) (TargetedTimeRange, bool) {
	var nextPeriod TargetedTimeRange
	found := false
	minDiff := 25 * time.Hour

	for _, p := range periods {
		if p.Start.After(fromTime) {
			diff := p.Start.Sub(fromTime)
			if diff < minDiff {
				minDiff = diff
				nextPeriod = p
				found = true
			}
		}
	}
	return nextPeriod, found
}

// findImmediateNextPeriod 查找紧接着的下一个时段（用于检测连续禁补）
func findImmediateNextPeriod(afterTime time.Time, periods []TimeRange) (TimeRange, bool) {
	var nextPeriod TimeRange
	found := false
	minDiff := 25 * time.Hour

	for _, p := range periods {
		if p.Start.After(afterTime) || p.Start.Equal(afterTime) {
			diff := p.Start.Sub(afterTime)
			if diff < minDiff {
				minDiff = diff
				nextPeriod = p
				found = true
			}
		}
	}

	return nextPeriod, found
}

// SumPredictedUsage 计算一个时间段内的预测总用水量
// 【重要修复】支持部分块的正确计算
// @param currentTime    当前实际时间（可能在块中间，如09:30）
// @param start          需要计算总用水量的开始时间
// @param end            需要计算总用水量的结束时间
// @param prediction3h   从blockStartTime开始的预测数组（如果当前在部分块中，[0]是调整后的值）
func SumPredictedUsage(currentTime, start, end time.Time, prediction3h []float64) float64 {
	if !end.After(start) {
		return 0.0
	}

	if start.Before(currentTime) {
		start = currentTime
	}

	totalUsage := 0.0

	// 【关键修复】第一个预测块从currentTime开始，后续块从标准边界开始
	// 计算第一个块的结束时间（对齐到下一个3小时整点）
	firstBlockEnd := getNextStandardTimeBlock(currentTime)

	// 使用currentTime作为第0块的起始基准
	startOffsetHours := start.Sub(currentTime).Hours()
	startIndex := int(math.Floor(divide(startOffsetHours, 3.0)))

	endOffsetHours := end.Sub(currentTime).Hours()
	endIndex := int(math.Floor(divide(endOffsetHours, 3.0)))

	if startIndex >= len(prediction3h) {
		if len(prediction3h) == 0 {
			logMessage(LogLevelWarn, "Warning: No prediction data available. Cannot estimate usage.\n")
			return 0.0
		}

		lastValue := prediction3h[len(prediction3h)-1]
		estimatedUsage := multiply(lastValue, divide(end.Sub(start).Hours(), 3.0))
		logMessage(LogLevelWarn, "Warning: Usage prediction beyond available data (start index %d >= length %d). Estimating %.2f m³ with last value.\n",
			startIndex, len(prediction3h), estimatedUsage)
		return round2(estimatedUsage)
	}

	needsExtrapolation := false
	extrapolationStart := time.Time{}

	if endIndex >= len(prediction3h) {
		// 外推意味着：预测数据不足。例如：currentTime: 2023-10-27 09:30:00 (预测生成的时间)、prediction3h: []float64{4.5, 6.0} (只有两个预测块)，分别为（09:30 - 12:00、 12:00 - 15:00）
		// 在上述情况下，用户发起的查询为：start: 2023-10-27 16:00:00、end: 2023-10-27 17:00:00
		needsExtrapolation = true
		// 外推起点：第一块结束时间 + 剩余完整块数量 * 3小时
		extrapolationStart = firstBlockEnd.Add(time.Duration(len(prediction3h)-1) * time.Hour * 3)
		endIndex = len(prediction3h) - 1
		logMessage(LogLevelDebug, "Notice: End time extends beyond prediction data. Will extrapolate from %s.\n", extrapolationStart.Format(HourLayout))
	}

	if startIndex == endIndex {
		// start 和 end 在同一个块内
		blockUsage := prediction3h[startIndex]

		// 【修复】计算块的实际边界
		var blockStart, blockEnd time.Time
		if startIndex == 0 {
			// 第一个块：从currentTime到firstBlockEnd
			blockStart = currentTime
			blockEnd = firstBlockEnd
		} else {
			// 后续块：标准3小时块
			blockStart = firstBlockEnd.Add(time.Duration(startIndex-1) * time.Hour * 3)
			blockEnd = blockStart.Add(time.Hour * 3)
		}

		// 计算实际使用的时间段
		actualStart := start
		if actualStart.Before(blockStart) {
			actualStart = blockStart
		}
		actualEnd := end
		if actualEnd.After(blockEnd) {
			actualEnd = blockEnd
		}

		// 计算占该块的比例
		usedDuration := actualEnd.Sub(actualStart).Hours()
		blockDuration := blockEnd.Sub(blockStart).Hours()
		proportion := divide(usedDuration, blockDuration)

		totalUsage = multiply(blockUsage, proportion)

	} else {
		// 跨越多个块

		// 1. 处理起始块（部分使用）
		if startIndex < len(prediction3h) {
			var blockStart, blockEnd time.Time
			if startIndex == 0 {
				// 第一个块：从currentTime到firstBlockEnd
				blockStart = currentTime
				blockEnd = firstBlockEnd
			} else {
				// 后续块：标准3小时块
				blockStart = firstBlockEnd.Add(time.Duration(startIndex-1) * time.Hour * 3)
				blockEnd = blockStart.Add(time.Hour * 3)
			}

			actualStart := start
			if actualStart.Before(blockStart) {
				actualStart = blockStart
			}

			usedDuration := blockEnd.Sub(actualStart).Hours()
			blockDuration := blockEnd.Sub(blockStart).Hours()
			proportionStart := divide(usedDuration, blockDuration)

			totalUsage = round2(totalUsage + multiply(prediction3h[startIndex], proportionStart))
		}

		// 2. 处理中间的完整块
		for i := startIndex + 1; i < endIndex; i++ {
			if i < len(prediction3h) {
				totalUsage = round2(totalUsage + prediction3h[i])
			}
		}

		// 3. 处理结束块（部分使用）
		if endIndex < len(prediction3h) {
			var blockStart, blockEnd time.Time
			if endIndex == 0 {
				// 第一个块：从currentTime到firstBlockEnd
				blockStart = currentTime
				blockEnd = firstBlockEnd
			} else {
				// 后续块：标准3小时块
				blockStart = firstBlockEnd.Add(time.Duration(endIndex-1) * time.Hour * 3)
				blockEnd = blockStart.Add(time.Hour * 3)
			}

			actualEnd := end
			if needsExtrapolation && extrapolationStart.Before(end) {
				actualEnd = extrapolationStart
			}
			if actualEnd.After(blockEnd) {
				actualEnd = blockEnd
			}

			usedDuration := actualEnd.Sub(blockStart).Hours()
			blockDuration := blockEnd.Sub(blockStart).Hours()
			proportionEnd := divide(usedDuration, blockDuration)

			totalUsage = round2(totalUsage + multiply(prediction3h[endIndex], proportionEnd))
		}

		// 4. 处理外推部分
		if needsExtrapolation {
			lastValue := prediction3h[len(prediction3h)-1]
			extraDuration := end.Sub(extrapolationStart).Hours()
			extraUsage := multiply(lastValue, divide(extraDuration, 3.0))
			totalUsage = round2(totalUsage + extraUsage)
			logMessage(LogLevelDebug, "  - Extrapolated %.2f m³ for period beyond prediction data.\n", extraUsage)
		}
	}

	if totalUsage < 0 {
		return 0.0
	}

	return round2(totalUsage)
}

// intersectionDuration 计算两个时间段的交集
func intersectionDuration(r1, r2 TimeRange) time.Duration {
	latestStart := r1.Start
	if r2.Start.After(latestStart) {
		latestStart = r2.Start
	}

	earliestEnd := r1.End
	if r2.End.Before(earliestEnd) {
		earliestEnd = r2.End
	}

	if !latestStart.Before(earliestEnd) {
		return 0
	}

	return earliestEnd.Sub(latestStart)
}

// ============================================================================
// 【新增】全天规划器相关代码
// ============================================================================

// getCurrentStandardBlock 获取当前时间所在的标准3小时块
// 返回该块的开始时间、结束时间和索引
func getCurrentStandardBlock(currentTime time.Time) (time.Time, time.Time, int) {
	// 标准时间块的小时数
	standardHours := []int{0, 3, 6, 9, 12, 15, 18, 21}

	currentHour := currentTime.Hour()

	// 找到当前时间所在的标准块
	for i, hour := range standardHours {
		nextHour := 24
		if i < len(standardHours)-1 {
			nextHour = standardHours[i+1]
		}

		if currentHour >= hour && currentHour < nextHour {
			// 找到了当前所在的块
			blockStart := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(),
				hour, 0, 0, 0, currentTime.Location())
			blockEnd := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(),
				nextHour, 0, 0, 0, currentTime.Location())
			return blockStart, blockEnd, i
		}
	}

	// 不应该到这里
	return currentTime, currentTime, -1
}

// getNextStandardTimeBlock 获取下一个标准的3小时时间块的开始时间
func getNextStandardTimeBlock(currentTime time.Time) time.Time {
	// 标准时间块的小时数
	standardHours := []int{0, 3, 6, 9, 12, 15, 18, 21}

	currentHour := currentTime.Hour()

	// 查找严格大于当前小时的下一个标准时间块
	for _, hour := range standardHours {
		if hour > currentHour {
			// 找到当天的下一个标准时间块
			return time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(),
				hour, 0, 0, 0, currentTime.Location())
		}
	}

	// 如果当天没有更晚的标准时间块，返回第二天的00:00
	nextDay := currentTime.Add(24 * time.Hour)
	return time.Date(nextDay.Year(), nextDay.Month(), nextDay.Day(),
		0, 0, 0, 0, currentTime.Location())
}

// calculatePlanningPeriods 计算从指定时间到目标时间的规划周期
func calculatePlanningPeriods(startTime, endTime time.Time, periodDuration time.Duration) []PlanningPeriod {
	var periods []PlanningPeriod

	// 获取当前时间所在的标准块
	currentBlockStart, currentBlockEnd, currentBlockIndex := getCurrentStandardBlock(startTime)

	// 如果当前时间不在块的起始点，需要先添加部分块
	if !startTime.Equal(currentBlockStart) && startTime.Before(currentBlockEnd) && currentBlockEnd.Before(endTime.Add(time.Minute)) {
		// 添加部分时间块
		partialDuration := currentBlockEnd.Sub(startTime)
		usageFraction := divide(partialDuration.Hours(), 3.0)

		periods = append(periods, PlanningPeriod{
			Start:         startTime,
			End:           currentBlockEnd,
			IsPartial:     true,
			BlockIndex:    currentBlockIndex,
			BlockStart:    currentBlockStart,
			UsageFraction: usageFraction,
		})

		// 从下一个标准块开始继续规划
		startTime = currentBlockEnd
	}

	// 添加完整的标准时间块
	currentTime := getNextStandardTimeBlock(startTime)
	if startTime.Equal(currentBlockStart) || startTime.Equal(currentBlockEnd) {
		// 如果已经在标准块的起点，直接从这里开始
		currentTime = startTime
	}

	blockIndex := divide(float64(currentTime.Hour()), 3.0)

	for currentTime.Before(endTime) {
		blockEnd := currentTime.Add(periodDuration)

		periods = append(periods, PlanningPeriod{
			Start:         currentTime,
			End:           blockEnd,
			IsPartial:     false,
			BlockIndex:    int(blockIndex),
			BlockStart:    currentTime,
			UsageFraction: 1.0,
		})

		currentTime = blockEnd
		blockIndex++
	}

	return periods
}

// getPredictionIndex 根据当前时间计算在预测数组中的索引
func getPredictionIndex(dayStartTime, currentTime time.Time, blockDuration time.Duration) int {
	elapsed := currentTime.Sub(dayStartTime)
	index := int(elapsed / blockDuration)
	return index
}

// DailyPlanner 全天规划器
type DailyPlanner struct {
	planner                *Planner
	startTime              time.Time
	endTime                time.Time
	initialLevel           float64
	fullDayPrediction3h    []float64
	fullDayPrediction30min []float64
	nextDayPrediction3h    []float64
}

// NewDailyPlanner 创建一个全天规划器
func NewDailyPlanner(rules UserRules, startTime time.Time, initialLevel float64,
	prediction3h []float64, prediction30min []float64, nextDayPrediction3h []float64) *DailyPlanner {

	planner := NewPlanner(rules, MinSafeLevelM3, MaxSafeLevelM3, InflowRateM3PerHour)

	// 计算结束时间（当天24:00）
	endTime := time.Date(startTime.Year(), startTime.Month(), startTime.Day(),
		24, 0, 0, 0, startTime.Location())

	// 如果开始时间已经是24:00或之后，则结束时间设为第二天24:00
	if !startTime.Before(endTime) {
		endTime = endTime.Add(24 * time.Hour)
	}

	return &DailyPlanner{
		planner:                planner,
		startTime:              startTime,
		endTime:                endTime,
		initialLevel:           initialLevel,
		fullDayPrediction3h:    prediction3h,
		fullDayPrediction30min: prediction30min,
		nextDayPrediction3h:    nextDayPrediction3h,
	}
}

// RunSimulation 运行从startTime到24:00的模拟
// 【优化说明】此函数中所有fmt打印语句已替换为logMessage调用
func (dp *DailyPlanner) RunSimulation() []*ReplenishmentTask {
	// 【优化】使用logTitle替代fmt.Println
	logTitle(LogLevelInfo, fmt.Sprintf("DAILY PLAN SIMULATION: %s -> 24:00", dp.startTime.Format(TimeLayout)))

	// 计算规划周期（包含部分块和完整块）
	planningPeriods := calculatePlanningPeriods(dp.startTime, dp.endTime, PlanningHorizon)

	if len(planningPeriods) == 0 {
		// 【优化】使用logMessage替代fmt.Println
		logMessage(LogLevelInfo, "No planning periods needed (already at or past 24:00)\n")
		return nil
	}

	// 【优化】使用logMessage替代fmt.Printf
	logMessage(LogLevelInfo, "Start time: %s\n", dp.startTime.Format(TimeLayout))
	if len(planningPeriods) > 0 {
		if planningPeriods[0].IsPartial {
			// 【优化】详细信息降为DEBUG级别
			logMessage(LogLevelDebug, "First period: %s -> %s (PARTIAL BLOCK, %.1f%% of full block)\n",
				planningPeriods[0].Start.Format(HourLayout),
				planningPeriods[0].End.Format(HourLayout),
				multiply(planningPeriods[0].UsageFraction, 100))
			logMessage(LogLevelDebug, "  Block time range: %s -> %s\n",
				planningPeriods[0].BlockStart.Format(HourLayout),
				planningPeriods[0].End.Format(HourLayout))
		} else {
			logMessage(LogLevelDebug, "First period: %s -> %s (FULL BLOCK)\n",
				planningPeriods[0].Start.Format(HourLayout),
				planningPeriods[0].End.Format(HourLayout))
		}
	}
	logMessage(LogLevelInfo, "Total planning periods: %d\n", len(planningPeriods))
	// 【优化】使用logSubTitle替代fmt.Println
	logSubTitle(LogLevelInfo)

	allTasks := []*ReplenishmentTask{}
	simulatedLevel := dp.initialLevel

	// 获取当天00:00的时间，用于计算索引
	//dayStart := time.Date(dp.startTime.Year(), dp.startTime.Month(), dp.startTime.Day(),
	//	0, 0, 0, 0, dp.startTime.Location())

	for i, period := range planningPeriods {
		// 【关键修改】准备调整后的预测数据
		adjustedPrediction3h := make([]float64, len(dp.fullDayPrediction3h)-period.BlockIndex)

		if period.BlockIndex < len(dp.fullDayPrediction3h) {
			// 复制从当前块开始的所有预测值
			copy(adjustedPrediction3h, dp.fullDayPrediction3h[period.BlockIndex:])

			// 如果是部分块，调整第一个预测值
			if period.IsPartial && len(adjustedPrediction3h) > 0 {
				originalBlockUsage := dp.fullDayPrediction3h[period.BlockIndex]
				// 只保留剩余时间的用水量，使用multiply确保保留2位小数
				adjustedPrediction3h[0] = multiply(originalBlockUsage, period.UsageFraction)

				// 【优化】调整信息使用DEBUG级别
				logMessage(LogLevelDebug, "  [Adjustment] Block %d (%s-%s): Full usage=%.2f m³, Remaining %.1f%% (%.2f m³)\n",
					period.BlockIndex,
					period.BlockStart.Format(HourLayout),
					period.End.Format(HourLayout),
					originalBlockUsage,
					multiply(period.UsageFraction, 100),
					adjustedPrediction3h[0])
			}
		} else {
			// 如果索引超出范围，使用最后一个值估算
			lastValue := 2.0
			if len(dp.fullDayPrediction3h) > 0 {
				lastValue = dp.fullDayPrediction3h[len(dp.fullDayPrediction3h)-1]
			}
			if period.IsPartial {
				lastValue = multiply(lastValue, period.UsageFraction)
			}
			adjustedPrediction3h = []float64{lastValue, lastValue, lastValue}
		}

		// 构建系统状态，添加BlockStartTime字段
		currentState := SystemState{
			CurrentTime:         period.Start,
			CurrentLevelM3:      simulatedLevel,
			Prediction3h:        adjustedPrediction3h,
			Prediction30min:     dp.fullDayPrediction30min,
			NextDayPrediction3h: dp.nextDayPrediction3h,
			BlockStartTime:      period.BlockStart, // 【关键】传入块的起始时间
		}

		// 判断是否是最后一个周期（21:00-24:00）
		isLastPeriod := period.BlockIndex == 7

		var periodTasks []*ReplenishmentTask
		if isLastPeriod {
			periodTasks = dp.planner.GenerateLastPlan(currentState)
		} else {
			periodTasks = dp.planner.GeneratePlan(currentState)
		}

		// 计算本周期的实际用水量（用于模拟）
		var usageInPeriod float64
		if period.BlockIndex < len(dp.fullDayPrediction3h) {
			baseUsage := dp.fullDayPrediction3h[period.BlockIndex]
			// 按比例计算实际用水量
			usageInPeriod = multiply(baseUsage, period.UsageFraction)
		} else {
			// 使用最后一个预测值
			if len(dp.fullDayPrediction3h) > 0 {
				baseUsage := dp.fullDayPrediction3h[len(dp.fullDayPrediction3h)-1]
				usageInPeriod = multiply(baseUsage, period.UsageFraction)
			} else {
				usageInPeriod = multiply(2.0, period.UsageFraction)
			}
		}

		refillInPeriod := 0.0
		if periodTasks != nil && len(periodTasks) > 0 {
			// 将所有子任务添加到总任务列表
			allTasks = append(allTasks, periodTasks...)

			// 计算本周期内所有任务的总补水量
			for _, task := range periodTasks {
				refillInPeriod = round2(refillInPeriod + task.AmountM3)

				// 【优化】任务生成信息使用INFO级别
				logMessage(LogLevelInfo, "=> Generated Task: [%s] Replenish %.2f m³ from %s to %s (%s)\n",
					task.Priority, task.AmountM3,
					task.StartTime.Format(HourLayout), task.EndTime.Format(HourLayout),
					task.Reason)
			}
		} else {
			// 【优化】无任务信息降为DEBUG级别
			logMessage(LogLevelDebug, "=> No task generated for this period.\n")
		}

		// 更新模拟液位
		nextSimulatedLevel := round2(simulatedLevel - usageInPeriod + refillInPeriod)

		// 物理限制
		if nextSimulatedLevel > MaxSafeLevelM3 {
			nextSimulatedLevel = MaxSafeLevelM3
		}
		if nextSimulatedLevel < 0 {
			nextSimulatedLevel = 0
		}

		blockType := "FULL"
		if period.IsPartial {
			blockType = fmt.Sprintf("PARTIAL(%.0f%%)", multiply(period.UsageFraction, 100))
		}

		// 【优化】周期统计使用INFO级别
		logMessage(LogLevelInfo, "Period %d [%s -> %s] %s: StartLevel=%.2f, Usage=%.2f, Refill=%.2f, EndLevel=%.2f\n",
			i+1,
			period.Start.Format(HourLayout),
			period.End.Format(HourLayout),
			blockType,
			simulatedLevel,
			usageInPeriod,
			refillInPeriod,
			nextSimulatedLevel,
		)
		// 【优化】使用logSubTitle替代fmt.Println
		logSubTitle(LogLevelInfo)

		simulatedLevel = nextSimulatedLevel
	}

	// 【优化】使用logTitle替代fmt.Println
	logTitle(LogLevelInfo, fmt.Sprintf("SIMULATION COMPLETE - Final Level: %.2f m³", simulatedLevel))

	return allTasks
}
